package br.ufrn.btest.criterias;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import br.ufrn.btest.exceptions.InvalidInputDataException;

public class Pairwise extends Criteria {

	public final static String EMPTY = "_";
	
	
	
	public Pairwise(List<List<String>> parametersInputValues) {
		super(parametersInputValues);
	}

	
	
	@Override
	public List<List<String>> getCombinations() throws InvalidInputDataException {

		if (getParametersInputValues().isEmpty() || getParametersInputValues().size() < 2) {
			throw new InvalidInputDataException();
		} else {
			List<List<String>> initial = initialize(getParametersInputValues().get(0), getParametersInputValues().get(1));
			List<List<String>> combinations = new ArrayList<List<String>>();

			if (getParametersInputValues().size() <= 2) {
				combinations.addAll(initial);
				return combinations;
			} else {
				return inParameterOrderGeneration(initial);
			}
		}
		
	}

	
	
	private List<List<String>> inParameterOrderGeneration(List<List<String>> initialPairs) {
		List<List<String>> combinations = null;
		for (int paramIndex = 2; paramIndex < getParametersInputValues().size(); paramIndex++) {
			PairCollection uncoveredPairs = horizontalGrowth(initialPairs, getParametersInputValues().get(paramIndex), getParametersInputValues().subList(0, paramIndex));
			combinations = new ArrayList<List<String>>();
			combinations.addAll(verticalGrowth(initialPairs, uncoveredPairs, paramIndex));
			initialPairs = combinations;
		}
		return combinations;
	}

	private Set<List<String>> verticalGrowth(List<List<String>> combinations, PairCollection uncoveredPairs, int paramIndex) {
		List<List<String>> tempCombinations = new ArrayList<List<String>>();

		for (TestPair testPair : uncoveredPairs.getPairs()) {
			List<String> comb = createCombinationForVerticalGrowth(paramIndex);
			comb.set(testPair.getValueAIndex(), testPair.getValueA());
			comb.set(testPair.getValueBIndex(), testPair.getValueB());
			tempCombinations.add(comb);
		}

		for (TestPair testPair : uncoveredPairs.getPairs()) {
			for (List<String> combination : tempCombinations) {
				boolean growingParamEqualsCombination = combination.get(testPair.getValueAIndex()).equals(testPair.getValueA());
				boolean otherValueIsEmpty = combination.get(testPair.getValueBIndex()).equals(EMPTY);
				if (growingParamEqualsCombination && otherValueIsEmpty) {
					combination.set(testPair.getValueBIndex(), testPair.getValueB());
				}
			}
		}

		for (List<String> combination : tempCombinations) {
			if (combination.contains(EMPTY)) {
				replaceEmptyValues(combination);
			}
		}

		Set<List<String>> newCombinations = new HashSet<List<String>>();
		newCombinations.addAll(combinations);
		newCombinations.addAll(tempCombinations);
		
		return newCombinations;
	}

	private void replaceEmptyValues(List<String> combination) {
		for (int i = 0; i < combination.size(); i++) {
			if (combination.get(i).equals(EMPTY)) {
				combination.set(i, getParametersInputValues().get(i).get(0));
			}
		}
	}

	private List<String> createCombinationForVerticalGrowth(int paramIndex) {
		List<String> combination = new ArrayList<String>(3);
		for (int i = 0; i <= paramIndex; i++) {
			combination.add(EMPTY);
		}
		return combination;
	}

	
	
	private PairCollection horizontalGrowth(List<List<String>> initialPairs, List<String> inputValuesForGrowth, List<List<String>> previouslyGrownValues) {
		
		PairCollection uncoveredPairs = new PairCollection(inputValuesForGrowth, previouslyGrownValues, previouslyGrownValues.size());

		if (initialPairs.size() <= inputValuesForGrowth.size()) {
			return growCombinationsAndRemoveCoveredPairs(initialPairs, inputValuesForGrowth, uncoveredPairs);
		} else {
			PairCollection uncoveredCombinations = growCombinationsAndRemoveCoveredPairs(initialPairs, inputValuesForGrowth, uncoveredPairs);
			
			for(int i = inputValuesForGrowth.size(); i < initialPairs.size(); i++) {
				List<String> newCombination = uncoveredPairs.getCombinationsThatCoversMostPairs(initialPairs.get(i), inputValuesForGrowth);
				initialPairs.set(i, newCombination);
				uncoveredPairs.removePairsCoveredBy(newCombination);
			}

			return uncoveredCombinations;
		}
		
	}

	
	
	private PairCollection growCombinationsAndRemoveCoveredPairs(List<List<String>> combinations, List<String> inputValuesForGrowth, PairCollection uncoveredPairs) {
		
		for(int i = 0; i < combinations.size(); i++) {
			if(i >= inputValuesForGrowth.size()) {
				break;
			} else {
				combinations.get(i).add(inputValuesForGrowth.get(i));
				uncoveredPairs.removePairsCoveredBy(combinations.get(i));
			}
		}
		
		return uncoveredPairs;
		
	}

//	private void printCombinations(List<List<String>> combinations) {
//		System.out.println("Combinations: ");
//		int i = 1;
//		for (List<String> combination : combinations) {
//			System.out.print(i + ": ");
//			for (String value : combination) {
//				System.out.print(value + ", ");
//			}
//			System.out.println();
//			i++;
//		}
//	}

	private List<List<String>> initialize(List<String> firstParamValues, List<String> secondParamValues) {
		List<List<String>> initialCombinations = new ArrayList<List<String>>();

		for (String firstParamValue : firstParamValues) {
			for (String secondParamValue : secondParamValues) {
				List<String> combination = new ArrayList<String>();
				combination.add(firstParamValue);
				combination.add(secondParamValue);
				initialCombinations.add(combination);
			}
		}

		return initialCombinations;
	}

//	private void printUncoveredPairs(PairCollection uncoveredPairs) {
//		System.out.println("UNCOVERED: ");
//		List<TestPair> pairs = uncoveredPairs.getPairs();
//		for (TestPair pair : pairs) {
//			System.out.println(pair.getValueA() + "=>" + pair.getValueAIndex() + ", " + pair.getValueB() + "=>" + pair.getValueBIndex());
//		}
//	}
}
